# *** author zhaomin
# 认证中间件
# 可以根据配置全局控制认证行为
# """ 


## 两种方式  
# 一、中间件的写法（除白名单和特定接口 都需要过一遍中间件的认证）    
# 二、依赖注入 depend的写法 （可以直接控制某个路由需不需要权限认证）

from typing import Optional
from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.types import ASGIApp

from app.core.config import settings
from app.utils.user import verify_token
from app.core.user import CTX_USER_ID
from app.utils.request import RequestHandler


class AuthMiddleware(BaseHTTPMiddleware):
    """
    认证中间件
    根据配置决定是否需要全局认证
    """
    
    def __init__(self, app: ASGIApp, exclude_paths: Optional[list] = None):
        """
        初始化认证中间件
        
        Args:
            app: ASGI应用
            exclude_paths: 额外的排除路径列表
        """
        super().__init__(app)
        self.exclude_paths = exclude_paths or []
        # 合并配置中的白名单和额外排除路径（可以传入）  
        self.all_exclude_paths = settings.auth_whitelist + self.exclude_paths
    
    async def dispatch(self, request: Request, call_next):
        """
        中间件处理逻辑
        
        Args:
            request: 请求对象
            call_next: 下一个处理函数
            
        Returns:
            Response: 响应对象
        """
        # 如果全局禁用认证，直接跳过
        if not settings.enable_auth:
            return await call_next(request)
        
        # 检查路径是否在排除列表中
        if request.url.path in self.all_exclude_paths:
            return await call_next(request)
        

        # 检查是否有Authorization header
        auth_header = request.headers.get("Authorization")
        
        if not auth_header or not auth_header.startswith("Bearer "):
            return RequestHandler.unauthorized(
                message="缺少认证凭证",
                remarks="请在请求头中提供有效的Authorization Bearer token"
            )
        
        # 提取token
        token = auth_header.split(" ")[1]
        
        # 验证token
        user_payload = verify_token(token)
        
        if not user_payload:
            return RequestHandler.unauthorized(
                message="无效的token或token已过期",
                remarks="请检查token是否正确或是否已过期"
            )
        
        # 设置用户上下文
        CTX_USER_ID.set(user_payload.user_id)
        
        # 将用户信息添加到request state中，供后续使用
        request.state.current_user = user_payload
        
        return await call_next(request)


class OptionalAuthMiddleware(BaseHTTPMiddleware):
    """
    可选认证中间件
    尝试解析token，但不强制要求
    """
    
    def __init__(self, app: ASGIApp):
        super().__init__(app)
    
    async def dispatch(self, request: Request, call_next):
        """
        中间件处理逻辑
        
        Args:
            request: 请求对象
            call_next: 下一个处理函数
            
        Returns:
            Response: 响应对象
        """
        # 如果全局禁用认证，直接跳过
        if not settings.enable_auth:
            request.state.current_user = None
            return await call_next(request)
        
        # 尝试解析token
        auth_header = request.headers.get("Authorization")
        current_user = None
        
        if auth_header and auth_header.startswith("Bearer "):
            token = auth_header.split(" ")[1]
            user_payload = verify_token(token)
            if user_payload:
                current_user = user_payload
                CTX_USER_ID.set(user_payload.user_id)
        
        # 将用户信息添加到request state中（可能为None）
        request.state.current_user = current_user
        
        return await call_next(request)


def setup_auth_middleware(app, middleware_type: str = "strict", exclude_paths: Optional[list] = None):
    """
    设置认证中间件的辅助函数
    
    Args:
        app: FastAPI应用实例
        middleware_type: 中间件类型，'strict'（严格）或'optional'（可选）
        exclude_paths: 额外的排除路径列表
    """
    if middleware_type == "strict":
        app.add_middleware(AuthMiddleware, exclude_paths=exclude_paths)
    elif middleware_type == "optional":
        app.add_middleware(OptionalAuthMiddleware)
    else:
        raise ValueError("middleware_type必须是'strict'或'optional'")


# 辅助函数：从request中获取当前用户
def get_user_from_request(request: Request) -> Optional[dict]:
    """
    从请求对象中获取当前用户信息
    
    Args:
        request: FastAPI请求对象
        
    Returns:
        Optional[dict]: 用户信息，如果没有则返回None
    """
    return getattr(request.state, 'current_user', None)